package c.x.jy.wexin.config;

import java.io.IOException;
import java.time.Duration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.RedisPassword;
import org.springframework.data.redis.connection.RedisStandaloneConfiguration;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.SetOperations;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;

import c.x.jy.service.redis.Cache;
import c.x.jy.service.redis.MemCacheImpl;
import c.x.jy.service.redis.RedisCacheImpl;
import c.x.jy.service.redis.RedisConstant;
import c.x.jy.wexin.config.model.JedisConf;
import redis.clients.jedis.JedisPoolConfig;

/**
 * Redis缓存配置类
 * 
 *
 */
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

	// @Value("${spring.redis.host}")
	// private String host;
	// @Value("${spring.redis.port}")
	// private int port;
	@Value("${spring.redis.timeout}")
	private int timeout; 

	@Value("${spring.redis.open: false}")
	private boolean open;
	@Autowired
	private RedisConnectionFactory factory;

	// 自定义缓存key生成策略
	@Bean
	public KeyGenerator keyGenerator() {
		return new KeyGenerator() {
			@Override
			public Object generate(Object target, java.lang.reflect.Method method, Object... params) {
				StringBuffer sb = new StringBuffer();
				sb.append(target.getClass().getName());
				sb.append(method.getName());
				for (Object obj : params) {
					sb.append(obj.toString());
				}
				return sb.toString();
			}
		};
	}

	/**
	 * 设置RedisCacheManager 使用cache注解管理redis缓存
	 *
	 * @return
	 */
	// 缓存管理器
	@SuppressWarnings({ "unchecked", "rawtypes" })
	@Bean
	public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
		// spring cache注解序列化配置
		RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
				.serializeKeysWith(
						RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getKeySerializer())) // key序列化方式
				.serializeValuesWith(
						RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer())) // value序列化方式
				.disableCachingNullValues() // 不缓存null值
				.entryTtl(Duration.ofSeconds(RedisConstant.DEFAULT_EXPIRE)); // 默认缓存过期时间

		// 设置一个初始化的缓存名称set集合
		Set<String> cacheNames = new HashSet<>();
		// 对每个缓存名称应用不同的配置，自定义过期时间
		Map<String, RedisCacheConfiguration> configMap = new HashMap<>();
		for (Iterator<Entry<String, Integer>> it = RedisConstant.redisValues.entrySet().iterator(); it.hasNext();) {
			Entry<String, Integer> entry = it.next();
			cacheNames.add(entry.getKey());
			configMap.put(entry.getKey(), redisCacheConfiguration.entryTtl(Duration.ofSeconds(entry.getValue())));
		}

		RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisTemplate.getConnectionFactory())
				.cacheDefaults(redisCacheConfiguration).transactionAware().initialCacheNames(cacheNames) // 注意这两句的调用顺序，一定要先调用该方法设置初始化的缓存名，再初始化相关的配置
				.withInitialCacheConfigurations(configMap).build();

		// redisCacheManager.afterPropertiesSet();
		return redisCacheManager;
	}

	@Bean
	public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
		RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
		redisTemplate.setKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashKeySerializer(new StringRedisSerializer());
		redisTemplate.setHashValueSerializer(new StringRedisSerializer());
		// redisTemplate.setValueSerializer(new StringRedisSerializer());
		redisTemplate.setValueSerializer(getSerializer());
		redisTemplate.setConnectionFactory(factory);
		// System.out.println("timeout:" + timeout);
		return redisTemplate;
	}

	@SuppressWarnings({ "rawtypes", "deprecation", "unchecked" })
	private Jackson2JsonRedisSerializer getSerializer() {

		Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
		ObjectMapper om = new ObjectMapper();
		om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
		om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
		jackson2JsonRedisSerializer.setObjectMapper(om);
		return jackson2JsonRedisSerializer;
		// template.setValueSerializer(jackson2JsonRedisSerializer);
	}

	@Bean
	@Primary
	public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig jedisPool,
			RedisStandaloneConfiguration jedisConfig) {
		JedisConnectionFactory connectionFactory = new JedisConnectionFactory(jedisConfig);
		connectionFactory.setPoolConfig(jedisPool);
		return connectionFactory;
	}

	@Bean
	public JedisPoolConfig jedisPool(JedisConf jedisConf) {
		JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
		jedisPoolConfig.setMaxIdle(jedisConf.getMaxIdle());
		jedisPoolConfig.setMaxWaitMillis(jedisConf.getMaxWait());
		jedisPoolConfig.setMaxTotal(jedisConf.getMaxActive());
		jedisPoolConfig.setMinIdle(jedisConf.getMinIdle());
		return jedisPoolConfig;
	}

	@Bean
	public RedisStandaloneConfiguration jedisConfig(JedisConf jedisConf) {
		RedisStandaloneConfiguration config = new RedisStandaloneConfiguration();
		config.setHostName(jedisConf.getHost());
		config.setPort(jedisConf.getPort());
		config.setDatabase(1);
		config.setPassword(RedisPassword.of(jedisConf.getPassword()));
		return config;
	}

	@Bean
	public HashOperations<String, String, Object> hashOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForHash();
	}

	@Bean
	public ValueOperations<String, String> valueOperations(RedisTemplate<String, String> redisTemplate) {
		return redisTemplate.opsForValue();
	}

	@Bean
	public ListOperations<String, Object> listOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForList();
	}

	@Bean
	public SetOperations<String, Object> setOperations(RedisTemplate<String, Object> redisTemplate) {
		return redisTemplate.opsForSet();
	}

	@Bean
	public ZSetOperations<String, Object> zSetOperations(RedisTemplate<String, Object> redisTemplate) {

		return redisTemplate.opsForZSet();
	}

	@Bean
	public RedisConnection RedisConnection() {
		return factory.getConnection();
	}
 
	@Bean
	public Cache cache() {
		Cache cache = null;
		if (open) {
			cache = new RedisCacheImpl();
		} else {
			cache = new MemCacheImpl();

		}
		return cache;

	}
	
	
	/*
	 * @Autowired private Environment env;
	 */
	 
	/*
	 * @Bean(destroyMethod = "shutdown") public RedissonClient redissonClient()
	 * throws IOException { String[] profiles = env.getActiveProfiles(); String
	 * profile = ""; if(profiles.length > 0) { profile = "-" + profiles[0]; } return
	 * Redisson.create( Config.fromYAML(new ClassPathResource("redisson" + profile +
	 * ".yml").getInputStream()) ); }
	 */
	
	  @Bean 
	    public RedissonClient redissonClientSingle(JedisConf jedisConf) throws IOException {
	        RedissonClient redisson = null;
	        Config config = new Config();
	        config.useSingleServer().setAddress("redis://" + jedisConf.getHost()+":"+jedisConf.getPort());
	        redisson = Redisson.create(config);
	        // 可通过打印redisson.getConfig().toJSON().toString()来检测是否配置成功
	        System.out.println("redissonConfig:"+ redisson.getConfig().toJSON().toString());
	        return redisson;
	    }
	
}